package com.hunter.api.help;

import java.util.Arrays;
import java.util.Base64;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

/**
 * ByteHelper
 * 字节数组相关方法
 *
 * @author HunterHuang
 * @version 1.0
 * @date 2018/9/12 11:06
 */
@Slf4j
public class ByteHelper {
    /**
     * byte数组转base64字符串
     *
     * @param textByte 要编码的字节数组
     * @return base64编码后的字符串
     */
    public static String encodeBase64(byte[] textByte) {
        String encodedText = null;
        try {
            final Base64.Encoder encoder = Base64.getEncoder();
            encodedText = encoder.encodeToString(textByte);
        } catch (Exception ex) {
            log.error("encodeBase64" + ex.getLocalizedMessage());
        }
        return encodedText;
    }

    /**
     * base64字符串转byte数组
     *
     * @param encodedText base64编码后的字符串
     * @return 解码后的字节数组
     */
    public static byte[] decodeBase64(String encodedText) {
        byte[] decodedBytes = null;
        final Base64.Decoder decoder = Base64.getDecoder();
        try {
            decodedBytes = decoder.decode(encodedText);
        } catch (Exception ex) {
            log.error("decodeBase64" + ex.getLocalizedMessage());
        }
        return decodedBytes;
    }

    /**
     *  url编码
     * @param textByte 要编码的字节数组
     * @return url编码后的字符串
     */
    public static String urlEncodeBase64(byte[] textByte) {
        String encodedText = null;
        try {
            final Base64.Encoder encoder = Base64.getUrlEncoder();
            encodedText = encoder.encodeToString(textByte);
        } catch (Exception ex) {
            log.error("encodeBase64" + ex.getLocalizedMessage());
        }
        return encodedText;
    }

    /**
     *  url解码
     * @param encodedText url编码后的字符串
     * @return 解码后的字节数组
     */
    public static byte[] urlDecodeBase64(String encodedText) {
        byte[] decodedBytes = null;
        final Base64.Decoder decoder = Base64.getUrlDecoder();
        try {
            decodedBytes = decoder.decode(encodedText);
        } catch (Exception ex) {
            log.error(" decodeBase64 " + ex.getLocalizedMessage());
        }
        return decodedBytes;
    }

    /**
     * 将bytes数组从index起始的len个字节，转换为大端序，原始字节序是originalSeq
     *
     * @param bytes 字节数组
     * @param index 起始位置
     * @param len 字节数量
     * @param originalSeq 原始字节序，有四种：0-大端序 1234，1-小端序 4321，2-modbus交换序：(大端交换)2143, 3-modbus交换序：(小端交换)3412
     *                    其中0和1，对2、4、8字节都有效；3和4只对4、8字节有效
     * @return 转换后的字节数组
     */
    public static byte[] getBigEndingBytes(byte[] bytes, int index, int len, int originalSeq) {
        if (len == 2) {
            byte[] newBytes = new byte[2];
            if (originalSeq == 0) {
                newBytes[0] = bytes[index];
                newBytes[1] = bytes[index + 1];
            } else {
                newBytes[0] = bytes[index + 1];
                newBytes[1] = bytes[index];
            }
            return newBytes;
        }

        if (len == 4) {
            byte[] newBytes = new byte[4];
            switch (originalSeq) {
                case 0:
                    newBytes[0] = bytes[index];
                    newBytes[1] = bytes[index + 1];
                    newBytes[2] = bytes[index + 2];
                    newBytes[3] = bytes[index + 3];
                    break;
                case 1:
                    newBytes[0] = bytes[index + 3];
                    newBytes[1] = bytes[index + 2];
                    newBytes[2] = bytes[index + 1];
                    newBytes[3] = bytes[index];
                    break;
                case 2:
                    newBytes[0] = bytes[index + 1];
                    newBytes[1] = bytes[index];
                    newBytes[2] = bytes[index + 3];
                    newBytes[3] = bytes[index + 2];
                    break;
                default:
                    newBytes[0] = bytes[index + 2];
                    newBytes[1] = bytes[index + 3];
                    newBytes[2] = bytes[index];
                    newBytes[3] = bytes[index + 1];
            }
            return newBytes;
        }

        if (len == 8) {
            byte[] newBytes = new byte[8];
            switch (originalSeq) {
                case 0:
                    newBytes[0] = bytes[index];
                    newBytes[1] = bytes[index + 1];
                    newBytes[2] = bytes[index + 2];
                    newBytes[3] = bytes[index + 3];
                    newBytes[4] = bytes[index + 4];
                    newBytes[5] = bytes[index + 5];
                    newBytes[6] = bytes[index + 6];
                    newBytes[7] = bytes[index + 7];
                    break;
                case 1:
                    newBytes[0] = bytes[index + 7];
                    newBytes[1] = bytes[index + 6];
                    newBytes[2] = bytes[index + 5];
                    newBytes[3] = bytes[index + 4];
                    newBytes[4] = bytes[index + 3];
                    newBytes[5] = bytes[index + 2];
                    newBytes[6] = bytes[index + 1];
                    newBytes[7] = bytes[index];
                    break;
                case 2:
                    newBytes[0] = bytes[index + 1];
                    newBytes[1] = bytes[index];
                    newBytes[2] = bytes[index + 3];
                    newBytes[3] = bytes[index + 2];
                    newBytes[4] = bytes[index + 5];
                    newBytes[5] = bytes[index + 4];
                    newBytes[6] = bytes[index + 7];
                    newBytes[7] = bytes[index + 6];
                    break;
                default:
                    newBytes[0] = bytes[index + 6];
                    newBytes[1] = bytes[index + 7];
                    newBytes[2] = bytes[index + 4];
                    newBytes[3] = bytes[index + 5];
                    newBytes[4] = bytes[index + 2];
                    newBytes[5] = bytes[index + 3];
                    newBytes[6] = bytes[index];
                    newBytes[7] = bytes[index + 1];
            }
            return newBytes;
        }
        return bytes;
    }


    /**
     * byte(int8)转换为byte[1]数组
     * 注意：java中char是2个字节！与byte、int8不一样
     *
     * @param c 一个字节
     * @return 转换后的字节数组
     */
    public static byte[] getBytes(byte c) {
        byte[] bytes = new byte[1];
        bytes[0] = c;
        return bytes;
    }

    /**
     * char(int16)转换为byte[2]数组,大端序
     * 注意：java中char是2个字节！与byte、int8不一样
     *
     * @param c 一个字符
     * @return 转换后的字节数组
     */
    public static byte[] getBytes(char c) {
        byte[] bytes = new byte[2];
        bytes[0] = (byte) ((c & 0xff00) >> 8);
        bytes[1] = (byte) (c & 0xff);
        return bytes;
    }

    /**
     * short(int16)转换为byte[2]数组,大端序
     *
     * @param s short
     * @return 转换后的字节数组
     */
    public static byte[] getBytes(short s) {
        byte[] bytes = new byte[2];
        bytes[0] = (byte) ((s & 0xff00) >> 8);
        bytes[1] = (byte) (s & 0xff);
        return bytes;
    }

    /**
     * int(int32)转换为byte[4]数组,大端序
     *
     * @param i int32
     * @return 转换后的字节数组
     */
    public static byte[] getBytes(int i) {
        byte[] bytes = new byte[4];
        bytes[0] = (byte) ((i & 0xff000000) >> 24);
        bytes[1] = (byte) ((i & 0xff0000) >> 16);
        bytes[2] = (byte) ((i & 0xff00) >> 8);
        bytes[3] = (byte) (i & 0xff);
        return bytes;
    }

    /**
     * long(int64)转换为byte[8]数组,大端序
     *
     * @param l int64
     * @return 转换后的字节数组
     */
    public static byte[] getBytes(long l) {
        byte[] bytes = new byte[8];
        bytes[0] = (byte) ((l >> 56) & 0xff);
        bytes[1] = (byte) ((l >> 48) & 0xff);
        bytes[2] = (byte) ((l >> 40) & 0xff);
        bytes[3] = (byte) ((l >> 32) & 0xff);
        bytes[4] = (byte) ((l >> 24) & 0xff);
        bytes[5] = (byte) ((l >> 16) & 0xff);
        bytes[6] = (byte) ((l >> 8) & 0xff);
        bytes[7] = (byte) (l & 0xff);
        return bytes;
    }


    /**
     * float转换为byte[4]数组,IEEE 754规范
     *
     * @param f float
     * @return 转换后的字节数组
     */
    public static byte[] getBytes(float f) {
        int intBits = Float.floatToIntBits(f);
        return getBytes(intBits);
    }

    /**
     * double转换为byte[8]数组,IEEE 754规范
     *
     * @param d double
     * @return 转换后的字节数组
     */
    public static byte[] getBytes(double d) {
        long lngBits = Double.doubleToLongBits(d);
        return getBytes(lngBits);
    }

    /**
     * 将uint8类型（ubyte,范围是0-255）转换为1个字节byte数组,如果超出范围，只保留低位，TODO: 待测试
     * 注意：java中char是2个字节！与byte、int8不一样
     *
     * @param uint8 uint8
     * @return 转换后的字节数组
     */
    public static byte[] getUInt8Bytes(int uint8) {
        byte[] bytes = new byte[1];
        bytes[0] = (byte) (uint8 & 0xff);
        return bytes;
    }

    /**
     * 将uint16类型（ushort,范围是0-65535）转换为2个字节byte数组,如果超出范围，只保留低位，TODO: 待测试
     *
     * @param uint16 uint16
     * @return 转换后的字节数组
     */
    public static byte[] getUInt16Bytes(int uint16) {
        int i = uint16 & 0xffff;
        byte[] bytes = new byte[2];
        bytes[0] = (byte) ((i & 0xff000000) >> 24);
        bytes[1] = (byte) ((i & 0xff0000) >> 16);
        return bytes;
    }

    /**
     * 将uint32类型（范围是0~4294967295）转换为4个字节byte数组,如果超出范围，只保留低位，TODO: 待测试
     *
     * @param uint32 uint32
     * @return 转换后的字节数组
     */
    public static byte[] getUIntBytes(long uint32) {
        long l = uint32 & 0xffffffff;
        byte[] bytes = new byte[4];
        bytes[0] = (byte) ((l >> 56) & 0xff);
        bytes[1] = (byte) ((l >> 48) & 0xff);
        bytes[2] = (byte) ((l >> 40) & 0xff);
        bytes[3] = (byte) ((l >> 32) & 0xff);
        return bytes;
    }

    /**
     * 从byte数组的index处的那个字节，获取bitOffset位置的值
     *
     * @param bytes 字节数组
     * @param index 起始位置
     * @param bitOffset 从左至右的第几位，0-7
     * @return bit值，0或1
     */
    public static int getBit(byte[] bytes, int index, int bitOffset) {
        return (bytes[index] >> (7 - bitOffset)) & 0x1;
    }

    /**
     * 从byte数组的index处的连续1个字节获得一个int8(byte)
     *
     * @param bytes 字节数组
     * @param index 起始位置
     * @return
     */
    public static int getInt8(byte[] bytes, int index) {
        return bytes[index];
    }

    /**
     * 从byte数组的index处的连续1个字节获得一个uint8(ubyte),TODO: 待测试
     *
     * @param bytes 字节数组
     * @param index 起始位置
     * @return
     */
    public static int getUInt8(byte[] bytes, int index) {
        return bytes[index] & 0xff;
    }

    /**
     * 从[大端序]byte数组的index处的连续两个字节获得一个short(int16)
     *
     * @param bytes 字节数组
     * @param index 起始位置
     * @return
     */
    public static short getShort(byte[] bytes, int index) {
        return (short) (0xff00 & bytes[index] << 8 | (0xff & bytes[index + 1]));
    }

    /**
     * 从[大端序]byte数组的index处的连续两个字节获得一个ushort(uint16),TODO: 待测试
     *
     * @param bytes 字节数组
     * @param index 起始位置
     * @return
     */
    public static int getUShort(byte[] bytes, int index) {
        return (0xff000000 & (bytes[index] << 24)) |
                (0x00ff0000 & (bytes[index + 1] << 16));
    }

    /**
     * 从[大端序]byte数组的index处的连续4个字节获得一个Int(int32)
     *
     * @param bytes 字节数组
     * @param index 起始位置
     * @return
     */
    public static int getInt(byte[] bytes, int index) {
        return (0xff000000 & (bytes[index] << 24)) |
                (0x00ff0000 & (bytes[index + 1] << 16)) |
                (0x0000ff00 & (bytes[index + 2] << 8)) |
                (0x000000ff & bytes[index + 3]);
    }

    /**
     * 从[大端序]byte数组的index处的连续4个字节获得一个UInt(uint32),TODO: 待测试
     *
     * @param bytes 字节数组
     * @param index 起始位置
     * @return
     */
    public static long getUInt(byte[] bytes, int index) {
        return (0xff00000000000000L & ((long) bytes[index] << 56)) |
                (0x00ff000000000000L & ((long) bytes[index + 1] << 48)) |
                (0x0000ff0000000000L & ((long) bytes[index + 2] << 40)) |
                (0x000000ff00000000L & ((long) bytes[index + 3] << 32));
    }

    /**
     * 从[大端序]byte数组的index处的连续8个字节获得一个long(int64)
     *
     * @param bytes 字节数组
     * @param index 起始位置
     * @return
     */
    public static long getLong(byte[] bytes, int index) {
        return (0xff00000000000000L & ((long) bytes[index] << 56)) |
                (0x00ff000000000000L & ((long) bytes[index + 1] << 48)) |
                (0x0000ff0000000000L & ((long) bytes[index + 2] << 40)) |
                (0x000000ff00000000L & ((long) bytes[index + 3] << 32)) |
                (0x00000000ff000000L & ((long) bytes[index + 4] << 24)) |
                (0x0000000000ff0000L & ((long) bytes[index + 5] << 16)) |
                (0x000000000000ff00L & ((long) bytes[index + 6] << 8)) |
                (0x00000000000000ffL & (long) bytes[index + 7]);
    }

    /**
     * 从byte数组的index处的连续4个字节获得一个float,IEEE 754规范
     *
     * @param bytes 字节数组
     * @param index 起始位置
     * @return
     */
    public static float getFloat(byte[] bytes, int index) {
        return Float.intBitsToFloat(getInt(bytes, index));
    }

    /**
     * 从byte数组的index处的连续8个字节获得一个double,IEEE 754规范
     *
     * @param bytes 字节数组
     * @param index 起始位置
     * @return
     */
    public static double getDouble(byte[] bytes, int index) {
        return Double.longBitsToDouble(getLong(bytes, index));
    }

    /**
     * 从byte数组，转换为hex字符串
     *
     * @param bytes 字节数组
     * @return
     */
    public static String getHexString(byte[] bytes) {
        return getHexString(bytes, 0, bytes.length, false);
    }

    /**
     * 从byte数组的index处的连续length个字节，按顺序转换为hex字符串
     *
     * @param bytes 字节数组
     * @param index 起始位置
     * @param length
     * @return
     */
    public static String getHexString(byte[] bytes, int index, int length) {
        return getHexString(bytes, index, length, false);
    }

    /**
     * 从byte数组的index处的连续length个字节，按顺序转换为hex字符串，可以在字节间加空格来分隔
     *
     * @param bytes    字节数组
     * @param index    起始位置
     * @param length   要转换的字节数
     * @param addBlank 每个Byte值之间是否加空格分隔
     * @return
     */
    public static String getHexString(byte[] bytes, int index, int length, boolean addBlank) {
        String stmp = "";
        StringBuilder sb = new StringBuilder("");
        for (int i = index; i < index + length; i++) {
            stmp = Integer.toHexString(bytes[i] & 0xFF);
            sb.append((stmp.length() == 1) ? "0" + stmp : stmp);
            if (addBlank) {
                //每个Byte值之间空格分隔
                sb.append(" ");
            }
        }
        return sb.toString().toUpperCase().trim();
    }

    /**
     * 将hex字符串转换成byte数组
     *
     * @param hexStr 16进制字符串,必须是合法的16进制字符串（0-9，a-f，偶数长度）
     * @return
     */
    public static byte[] getHexBytes(String hexStr) {
        if (StringUtils.isBlank(hexStr) || hexStr.trim().length() < 2) {
            return new byte[0];
        }

        hexStr = hexStr.trim();
        byte[] buf = new byte[hexStr.length() / 2];
        for (int i = 0; i < hexStr.length() / 2; i++) {
            String subStr = hexStr.substring(i * 2, i * 2 + 2);
            buf[i] = (byte) Integer.parseInt(subStr, 16);
        }

        return buf;
    }

    /**
     * bute数组，倒序：小端序的byte[] 转成大端序的byte[]；大端序的转成小端序
     *
     * @param bytes 字节数组
     * @return
     */
    public static byte[] reverseBytes(byte[] bytes) {
        int len = bytes.length;
        byte[] result = new byte[len];
        for (int i = 0; i < len; i++) {
            result[i] = bytes[len - 1 - i];
        }

        return result;
    }

    /**
     * 从byte数组的index处的连续length个字节，转换为新的byte数组
     *
     * @param bytes 字节数组
     * @param index 起始位置
     * @param length 字节数量
     * @return
     */
    public static byte[] subBytes(byte[] bytes, int index, int length) {
        byte[] bs = new byte[length];
        System.arraycopy(bytes, index, bs, 0, length);
        return bs;
    }

    /**
     * Convert char to byte
     *
     * @param c char
     * @return byte
     */
    private static byte charToByte(char c) {
        return (byte) "0123456789ABCDEF".indexOf(c);
    }

//    /**
//     * 将二进制字符串转换为二进制数组
//     *
//     * @param binaryStr
//     * @return
//     */
//    webroot static byte[] parseBinaryStr2Byte(String binaryStr) {
//        if (binaryStr.length() < 1)
//            return null;
//        int clen = binaryStr.length() % 8;
//        if (clen != 0) {
//            binaryStr = String.format("%0" + clen + "d", 0) + binaryStr;
//        }
//        byte[] result = new byte[binaryStr.length() / 8];
//        for (int i = 0; i < result.length; i++) {
//            result[i] = Byte.parseByte(binaryStr, 2);
//        }
//        return result;
//    }

//    /**
//     * 拆分数组为指定长度的多个数组
//     *
//     * @param src
//     * @param newBytes key=>length 如果不存在返回 null
//     * @return
//     */
//    webroot static JsonObject subBytes(byte[] src, JsonObject newBytes) {
//        JsonObject newBeans = new JsonObject();
//        int begin = 0;
//        for (String key : newBytes.getMap().keySet()) {
//            int length = newBytes.getInteger(key, 0);
//            byte[] tmpBytes = subBytes(src, begin, length);
//            begin += length;
//            newBeans.put(key, tmpBytes);
//        }
//        return newBeans;
//    }

    /**
     * 从bytes数组解析值
     *
     * @param bytes     byte数组
     * @param dataType 解析值的数据类型
     * @param offset    在byte数组中的起始位置
     * @param len       字节长度，只对9-hex类型有效
     * @param bitOffset 如果是bit类型，在字节中的位置
     * @param byteSeq   字节排序，对于2字节类型，只有两种情况：0-大端序 12，1-小端序 21
     *                  对于4字节类型，有4种情况：0-大端序 1234，1-小端序 4321，2-modbus交换序：(大端交换)2143, 3-modbus交换序：(小端交换)3412
     * @return
     */
    public static Object getValue(byte[] bytes, int dataType, int offset, int len, int bitOffset, int byteSeq) {
        Object value;
        switch (dataType) {
            case 0:
                //bit，parse_bit_offset
                value = getBit(bytes, offset, bitOffset);
                break;
            case 1:
                //uint8、uchar、ubyte
                value = getUInt8(bytes, offset);
                break;
            case 2:
                //int8、char、byte
                value = getInt8(bytes, offset);
                break;
            case 3:
                //uint16、ushort，parse_byte_seq
                len = 2;
                //字节排序，只有两种情况：0-大端序 12，1-小端序 21
                byte[] ui16 = getBigEndingBytes(bytes, offset, len, byteSeq);
                value = getUShort(ui16, 0);
                break;
            case 4:
                //int16、short
                len = 2;
                //字节排序，只有两种情况：0-大端序 12，1-小端序 21
                byte[] i16 = getBigEndingBytes(bytes, offset, len, byteSeq);
                value = getShort(i16, 0);
                break;
            case 5:
                //uint32
                len = 4;
                //字节排序，有4种情况：0-大端序 1234，1-小端序 4321，2-modbus交换序：(大端交换)2143, 3-modbus交换序：(小端交换)3412
                byte[] ui32 = getBigEndingBytes(bytes, offset, len, byteSeq);
                value = getUInt(ui32, 0);
                break;
            case 6:
                //int32
                len = 4;
                //字节排序，有4种情况：0-大端序 1234，1-小端序 4321，2-modbus交换序：(大端交换)2143, 3-modbus交换序：(小端交换)3412
                byte[] i32 = getBigEndingBytes(bytes, offset, len, byteSeq);
                value = getInt(i32, 0);
                break;
            case 7:
                //float
                len = 4;
                //字节排序，有4种情况：0-大端序 1234，1-小端序 4321，2-modbus交换序：(大端交换)2143, 3-modbus交换序：(小端交换)3412
                byte[] f = getBigEndingBytes(bytes, offset, len, byteSeq);
                value = getFloat(f, 0);
                break;
            case 8:
                //double
                len = 8;
                //字节排序，有4种情况：0-大端序 12345678，1-小端序 87654321， 2-modbus交换序：(大端交换)21436587, 3-modbus交换序：(小端交换)78563412
                byte[] d = getBigEndingBytes(bytes, offset, len, byteSeq);
                value = getDouble(d, 0);
                break;
            default:  //其它，都当作 9- hex
                //字节排序，只有两种情况：0-大端序 1234，1-小端序 4321
                byte[] newBytes = Arrays.copyOfRange(bytes, offset, offset + len);
                if (byteSeq == 1) {
                    newBytes = reverseBytes(newBytes);
                }

                value = getHexString(newBytes);
        }

        return value;
    }
}
